/******************************************************************************
*  Copyright(c) 2021-2023 Sietium
*
*  @file:    material.cpp
*  @brif:    diff material
*
*  @date:    05/10/2021
*  @Author:  cary.Zhang
*
******************************************************************************/
#include <string>
#include <cstdlib>
#include <memory>
#include "vmath.h"
#include "glad/gl-headers.h"

#include "glslprogram.h"
#include "texture.h"
#include "node.h"
#include "model.h"
#include "objectfactory.h"

class Material: public Node
{
public:
    Material();
protected:
    bool startup();
    void render(double currentTime, double difTime);
    void shutdown();
protected:
    GLSLProgram program;
    GLuint texEnvmap;
    GLuint envmaps[4];
    int envmapIndex;
    std::unique_ptr<Model> model;
};

Material::Material()
{
    nodeName = "Material";
}

bool Material::startup()
{
    if (PerfWindow::glVersion < 330) {
        printf("Failed! The GL version is less than 3.3 !\n");
        return false;
    }

    bool ok = program.build("../media/shaders/material/render.vs.glsl",
                            "../media/shaders/material/render.fs.glsl");
    if (!ok)
        return false;

    envmapIndex = 0;

    envmaps[0] = Texture::loadKtxImage("../media/textures/envmaps/spheremap1.ktx");
    envmaps[1] = Texture::loadKtxImage("../media/textures/envmaps/spheremap2.ktx");
    envmaps[2] = Texture::loadKtxImage("../media/textures/envmaps/spheremap3.ktx");
    texEnvmap = envmaps[envmapIndex];

    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CCW);
    glCullFace(GL_BACK);

    model.reset(new Model);
    model->loadModel("../media/objects/animal.obj");

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    return true;
}

void Material::render(double currentTime, double difTime)
{
    (void)difTime;
    static const GLfloat gray[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    static const GLfloat ones[] = { 1.0f };

    float temp = (totalTime + 2) / 3;
    envmapIndex = lastTime / temp;
    texEnvmap = envmaps[envmapIndex];

    glClearBufferfv(GL_COLOR, 0, gray);
    glClearBufferfv(GL_DEPTH, 0, ones);

    if (envmapIndex > 2 )
        texEnvmap = 0;

    glBindTexture(GL_TEXTURE_2D, texEnvmap);

    program.use();

    float f = currentTime * 1.5f;

    auto modelMatrix = vmath::scale(42.0f);
    auto viewPosition = vmath::vec3(cosf(f * 0.35f) * 120.0f, cosf(f * 0.4f) * 1.0f,
                                     sinf(f * 0.35f) * 120.0f);
    auto viewMatrix = vmath::lookat(viewPosition, vmath::vec3(0.0f, .0f, 0.0f), vmath::vec3(0.0f,
                                                                                              1.0f, 0.0f));
    auto mvMatrix = viewMatrix * modelMatrix;
    auto projMatrix = vmath::perspective(45.0f, (float)getWidth() / (float)getHeight(),
                                          0.1f,
                                          1000.0f);

    program.setUniformMatrix4fv("mv_matrix", mvMatrix);
    program.setUniformMatrix4fv("proj_matrix", projMatrix);

    model->renderModel();
}

void Material::shutdown()
{
    program.clear();
    model->clearModel();

    glBindTexture(GL_TEXTURE_2D, 0);
    glDepthFunc(GL_LESS);
    glDisable(GL_DEPTH_TEST);
    glUseProgram(0);
    glDeleteTextures(4, envmaps);
}

REGISTER_OBJECT(Node, Material)
